
TEST_CASE( MESH_PREFIX "Edge: Region of edge indices" ){
	using hms::EdgeRange;
	
	auto test = [](const auto& mesh){
		for (Index i{0}; i<mesh.nEdges(); ++i){
			EdgeRange r { whichRange(mesh,i).first };
			if (i<15)
				REQUIRE( r == EdgeRange::horizontal );
			else
				REQUIRE( r == EdgeRange::vertical );
		}
	};

	SECTION( "One ghost layer" ){
		MAKE_MESH(3,4,1,1)
		test(mesh);
	}
	// SECTION( "Two ghost layers" ){
	// 	MAKE_MESH(3,4,1,1)
	// 	test(mesh);
	// }
}

TEST_CASE( MESH_PREFIX "Edge: vertex indices" ){
	auto test = [](const auto& mesh){
		REQUIRE( (mesh.edge( 0).vertices() == Array2i{  0,  1}).all() );
		REQUIRE( (mesh.edge( 1).vertices() == Array2i{  1,  2}).all() );
		REQUIRE( (mesh.edge( 2).vertices() == Array2i{  2,  3}).all() );
		REQUIRE( (mesh.edge( 3).vertices() == Array2i{  4,  5}).all() );
		REQUIRE( (mesh.edge( 4).vertices() == Array2i{  5,  6}).all() );
		REQUIRE( (mesh.edge( 5).vertices() == Array2i{  6,  7}).all() );
		REQUIRE( (mesh.edge( 6).vertices() == Array2i{  8,  9}).all() );
		REQUIRE( (mesh.edge( 7).vertices() == Array2i{  9, 10}).all() );
		REQUIRE( (mesh.edge( 8).vertices() == Array2i{ 10, 11}).all() );
		REQUIRE( (mesh.edge( 9).vertices() == Array2i{ 12, 13}).all() );
		REQUIRE( (mesh.edge(10).vertices() == Array2i{ 13, 14}).all() );
		REQUIRE( (mesh.edge(11).vertices() == Array2i{ 14, 15}).all() );
		REQUIRE( (mesh.edge(12).vertices() == Array2i{ 16, 17}).all() );
		REQUIRE( (mesh.edge(13).vertices() == Array2i{ 17, 18}).all() );
		REQUIRE( (mesh.edge(14).vertices() == Array2i{ 18, 19}).all() );
		REQUIRE( (mesh.edge(15).vertices() == Array2i{  0,  4}).all() );
		REQUIRE( (mesh.edge(16).vertices() == Array2i{  1,  5}).all() );
		REQUIRE( (mesh.edge(17).vertices() == Array2i{  2,  6}).all() );
		REQUIRE( (mesh.edge(18).vertices() == Array2i{  3,  7}).all() );
		REQUIRE( (mesh.edge(19).vertices() == Array2i{  4,  8}).all() );
		REQUIRE( (mesh.edge(20).vertices() == Array2i{  5,  9}).all() );
		REQUIRE( (mesh.edge(21).vertices() == Array2i{  6, 10}).all() );
		REQUIRE( (mesh.edge(22).vertices() == Array2i{  7, 11}).all() );
		REQUIRE( (mesh.edge(23).vertices() == Array2i{  8, 12}).all() );
		REQUIRE( (mesh.edge(24).vertices() == Array2i{  9, 13}).all() );
		REQUIRE( (mesh.edge(25).vertices() == Array2i{ 10, 14}).all() );
		REQUIRE( (mesh.edge(26).vertices() == Array2i{ 11, 15}).all() );
		REQUIRE( (mesh.edge(27).vertices() == Array2i{ 12, 16}).all() );
		REQUIRE( (mesh.edge(28).vertices() == Array2i{ 13, 17}).all() );
		REQUIRE( (mesh.edge(29).vertices() == Array2i{ 14, 18}).all() );
		REQUIRE( (mesh.edge(30).vertices() == Array2i{ 15, 19}).all() );
	};

	SECTION("One ghost layer"){
		MAKE_MESH(3,4,1,1)
		test(mesh);
	}
}

TEST_CASE( MESH_PREFIX "Edge: lengths" ){
	
	auto test = []( const auto& mesh ){
		for (Index i{0}; i<15; ++i){
			REQUIRE( mesh.edge(i).length() == Approx(1.2) );
		}
		for (Index i{15}; i<30; ++i){
			REQUIRE( mesh.edge(i).length() == Approx(3.4) );
		}
	};

	SECTION("One ghost layer"){
		MAKE_MESH(3,4,1.2,3.4)
		test(mesh);
	}
}

TEST_CASE( MESH_PREFIX "Edge: normals" ){
	
	auto test = []( const auto& mesh ){
		for (Index i{0}; i<15; ++i){
			REQUIRE( mesh.edge(i).normal() == Vector2s{0,1} );
		}
		for (Index i{15}; i<30; ++i){
			REQUIRE( mesh.edge(i).normal() == Vector2s{1,0} );
		}
	};

	SECTION("One ghost layer"){
		MAKE_MESH(3,4,1,1)
		test(mesh);
	}
}

TEST_CASE( MESH_PREFIX "Edge: adjacent cells" ){
	auto testOneLayer = [](const MESH_TYPE& mesh){
		REQUIRE( (mesh.edge( 0).cells() == Array2i{ 1, 6}).all() );
		REQUIRE( (mesh.edge( 1).cells() == Array2i{ 2, 7}).all() );
		REQUIRE( (mesh.edge( 2).cells() == Array2i{ 3, 8}).all() );
		REQUIRE( (mesh.edge( 3).cells() == Array2i{ 6,11}).all() );
		REQUIRE( (mesh.edge( 4).cells() == Array2i{ 7,12}).all() );
		REQUIRE( (mesh.edge( 5).cells() == Array2i{ 8,13}).all() );
		REQUIRE( (mesh.edge( 6).cells() == Array2i{11,16}).all() );
		REQUIRE( (mesh.edge( 7).cells() == Array2i{12,17}).all() );
		REQUIRE( (mesh.edge( 8).cells() == Array2i{13,18}).all() );
		REQUIRE( (mesh.edge( 9).cells() == Array2i{16,21}).all() );
		REQUIRE( (mesh.edge(10).cells() == Array2i{17,22}).all() );
		REQUIRE( (mesh.edge(11).cells() == Array2i{18,23}).all() );
		REQUIRE( (mesh.edge(12).cells() == Array2i{21,26}).all() );
		REQUIRE( (mesh.edge(13).cells() == Array2i{22,27}).all() );
		REQUIRE( (mesh.edge(14).cells() == Array2i{23,28}).all() );
		REQUIRE( (mesh.edge(15).cells() == Array2i{ 5, 6}).all() );
		REQUIRE( (mesh.edge(16).cells() == Array2i{ 6, 7}).all() );
		REQUIRE( (mesh.edge(17).cells() == Array2i{ 7, 8}).all() );
		REQUIRE( (mesh.edge(18).cells() == Array2i{ 8, 9}).all() );
		REQUIRE( (mesh.edge(19).cells() == Array2i{10,11}).all() );
		REQUIRE( (mesh.edge(20).cells() == Array2i{11,12}).all() );
		REQUIRE( (mesh.edge(21).cells() == Array2i{12,13}).all() );
		REQUIRE( (mesh.edge(22).cells() == Array2i{13,14}).all() );
		REQUIRE( (mesh.edge(23).cells() == Array2i{15,16}).all() );
		REQUIRE( (mesh.edge(24).cells() == Array2i{16,17}).all() );
		REQUIRE( (mesh.edge(25).cells() == Array2i{17,18}).all() );
		REQUIRE( (mesh.edge(26).cells() == Array2i{18,19}).all() );
		REQUIRE( (mesh.edge(27).cells() == Array2i{20,21}).all() );
		REQUIRE( (mesh.edge(28).cells() == Array2i{21,22}).all() );
		REQUIRE( (mesh.edge(29).cells() == Array2i{22,23}).all() );
		REQUIRE( (mesh.edge(30).cells() == Array2i{23,24}).all() );
	};
	auto testTwoLayers = [](const MESH_TYPE& mesh){
		SECTION("First order neighbours"){
			REQUIRE( (mesh.edge( 0).cells(1) == Array2i{ 9,16}).all() );
			REQUIRE( (mesh.edge( 1).cells(1) == Array2i{10,17}).all() );
			REQUIRE( (mesh.edge( 2).cells(1) == Array2i{11,18}).all() );
			REQUIRE( (mesh.edge( 3).cells(1) == Array2i{16,23}).all() );
			REQUIRE( (mesh.edge( 4).cells(1) == Array2i{17,24}).all() );
			REQUIRE( (mesh.edge( 5).cells(1) == Array2i{18,25}).all() );
			REQUIRE( (mesh.edge( 6).cells(1) == Array2i{23,30}).all() );
			REQUIRE( (mesh.edge( 7).cells(1) == Array2i{24,31}).all() );
			REQUIRE( (mesh.edge( 8).cells(1) == Array2i{25,32}).all() );
			REQUIRE( (mesh.edge( 9).cells(1) == Array2i{30,37}).all() );
			REQUIRE( (mesh.edge(10).cells(1) == Array2i{31,38}).all() );
			REQUIRE( (mesh.edge(11).cells(1) == Array2i{32,39}).all() );
			REQUIRE( (mesh.edge(12).cells(1) == Array2i{37,44}).all() );
			REQUIRE( (mesh.edge(13).cells(1) == Array2i{38,45}).all() );
			REQUIRE( (mesh.edge(14).cells(1) == Array2i{39,46}).all() );
			REQUIRE( (mesh.edge(15).cells(1) == Array2i{15,16}).all() );
			REQUIRE( (mesh.edge(16).cells(1) == Array2i{16,17}).all() );
			REQUIRE( (mesh.edge(17).cells(1) == Array2i{17,18}).all() );
			REQUIRE( (mesh.edge(18).cells(1) == Array2i{18,19}).all() );
			REQUIRE( (mesh.edge(19).cells(1) == Array2i{22,23}).all() );
			REQUIRE( (mesh.edge(20).cells(1) == Array2i{23,24}).all() );
			REQUIRE( (mesh.edge(21).cells(1) == Array2i{24,25}).all() );
			REQUIRE( (mesh.edge(22).cells(1) == Array2i{25,26}).all() );
			REQUIRE( (mesh.edge(23).cells(1) == Array2i{29,30}).all() );
			REQUIRE( (mesh.edge(24).cells(1) == Array2i{30,31}).all() );
			REQUIRE( (mesh.edge(25).cells(1) == Array2i{31,32}).all() );
			REQUIRE( (mesh.edge(26).cells(1) == Array2i{32,33}).all() );
			REQUIRE( (mesh.edge(27).cells(1) == Array2i{36,37}).all() );
			REQUIRE( (mesh.edge(28).cells(1) == Array2i{37,38}).all() );
			REQUIRE( (mesh.edge(29).cells(1) == Array2i{38,39}).all() );
			REQUIRE( (mesh.edge(30).cells(1) == Array2i{39,40}).all() );
		}
		SECTION("Second order neighbours"){
			REQUIRE( (mesh.edge( 0).cells(2) == Array2i{ 2,23}).all() );
			REQUIRE( (mesh.edge( 1).cells(2) == Array2i{ 3,24}).all() );
			REQUIRE( (mesh.edge( 2).cells(2) == Array2i{ 4,25}).all() );
			REQUIRE( (mesh.edge( 3).cells(2) == Array2i{ 9,30}).all() );
			REQUIRE( (mesh.edge( 4).cells(2) == Array2i{10,31}).all() );
			REQUIRE( (mesh.edge( 5).cells(2) == Array2i{11,32}).all() );
			REQUIRE( (mesh.edge( 6).cells(2) == Array2i{16,37}).all() );
			REQUIRE( (mesh.edge( 7).cells(2) == Array2i{17,38}).all() );
			REQUIRE( (mesh.edge( 8).cells(2) == Array2i{18,39}).all() );
			REQUIRE( (mesh.edge( 9).cells(2) == Array2i{23,44}).all() );
			REQUIRE( (mesh.edge(10).cells(2) == Array2i{24,45}).all() );
			REQUIRE( (mesh.edge(11).cells(2) == Array2i{25,46}).all() );
			REQUIRE( (mesh.edge(12).cells(2) == Array2i{30,51}).all() );
			REQUIRE( (mesh.edge(13).cells(2) == Array2i{31,52}).all() );
			REQUIRE( (mesh.edge(14).cells(2) == Array2i{32,53}).all() );
			REQUIRE( (mesh.edge(15).cells(2) == Array2i{14,17}).all() );
			REQUIRE( (mesh.edge(16).cells(2) == Array2i{15,18}).all() );
			REQUIRE( (mesh.edge(17).cells(2) == Array2i{16,19}).all() );
			REQUIRE( (mesh.edge(18).cells(2) == Array2i{17,20}).all() );
			REQUIRE( (mesh.edge(19).cells(2) == Array2i{21,24}).all() );
			REQUIRE( (mesh.edge(20).cells(2) == Array2i{22,25}).all() );
			REQUIRE( (mesh.edge(21).cells(2) == Array2i{23,26}).all() );
			REQUIRE( (mesh.edge(22).cells(2) == Array2i{24,27}).all() );
			REQUIRE( (mesh.edge(23).cells(2) == Array2i{28,31}).all() );
			REQUIRE( (mesh.edge(24).cells(2) == Array2i{29,32}).all() );
			REQUIRE( (mesh.edge(25).cells(2) == Array2i{30,33}).all() );
			REQUIRE( (mesh.edge(26).cells(2) == Array2i{31,34}).all() );
			REQUIRE( (mesh.edge(27).cells(2) == Array2i{35,38}).all() );
			REQUIRE( (mesh.edge(28).cells(2) == Array2i{36,39}).all() );
			REQUIRE( (mesh.edge(29).cells(2) == Array2i{37,40}).all() );
			REQUIRE( (mesh.edge(30).cells(2) == Array2i{38,41}).all() );
		}
	};
	SECTION("One ghost layer"){
		MAKE_MESH(3,4,1,1)
		testOneLayer(mesh);
	}
}

TEST_CASE( MESH_PREFIX "Edge: midpoints" ){
	
	auto test = []( const auto& mesh ){
		REQUIRE( mesh.edge( 0).midpoint() == Vector2s{0.5,0.0} );
		REQUIRE( mesh.edge( 1).midpoint() == Vector2s{1.5,0.0} );
		REQUIRE( mesh.edge( 2).midpoint() == Vector2s{2.5,0.0} );
		REQUIRE( mesh.edge( 3).midpoint() == Vector2s{0.5,1.0} );
		REQUIRE( mesh.edge( 4).midpoint() == Vector2s{1.5,1.0} );
		REQUIRE( mesh.edge( 5).midpoint() == Vector2s{2.5,1.0} );
		REQUIRE( mesh.edge( 6).midpoint() == Vector2s{0.5,2.0} );
		REQUIRE( mesh.edge( 7).midpoint() == Vector2s{1.5,2.0} );
		REQUIRE( mesh.edge( 8).midpoint() == Vector2s{2.5,2.0} );
		REQUIRE( mesh.edge( 9).midpoint() == Vector2s{0.5,3.0} );
		REQUIRE( mesh.edge(10).midpoint() == Vector2s{1.5,3.0} );
		REQUIRE( mesh.edge(11).midpoint() == Vector2s{2.5,3.0} );
		REQUIRE( mesh.edge(12).midpoint() == Vector2s{0.5,4.0} );
		REQUIRE( mesh.edge(13).midpoint() == Vector2s{1.5,4.0} );
		REQUIRE( mesh.edge(14).midpoint() == Vector2s{2.5,4.0} );
		REQUIRE( mesh.edge(15).midpoint() == Vector2s{0.0,0.5} );
		REQUIRE( mesh.edge(16).midpoint() == Vector2s{1.0,0.5} );
		REQUIRE( mesh.edge(17).midpoint() == Vector2s{2.0,0.5} );
		REQUIRE( mesh.edge(18).midpoint() == Vector2s{3.0,0.5} );
		REQUIRE( mesh.edge(19).midpoint() == Vector2s{0.0,1.5} );
		REQUIRE( mesh.edge(20).midpoint() == Vector2s{1.0,1.5} );
		REQUIRE( mesh.edge(21).midpoint() == Vector2s{2.0,1.5} );
		REQUIRE( mesh.edge(22).midpoint() == Vector2s{3.0,1.5} );
		REQUIRE( mesh.edge(23).midpoint() == Vector2s{0.0,2.5} );
		REQUIRE( mesh.edge(24).midpoint() == Vector2s{1.0,2.5} );
		REQUIRE( mesh.edge(25).midpoint() == Vector2s{2.0,2.5} );
		REQUIRE( mesh.edge(26).midpoint() == Vector2s{3.0,2.5} );
		REQUIRE( mesh.edge(27).midpoint() == Vector2s{0.0,3.5} );
		REQUIRE( mesh.edge(28).midpoint() == Vector2s{1.0,3.5} );
		REQUIRE( mesh.edge(29).midpoint() == Vector2s{2.0,3.5} );
		REQUIRE( mesh.edge(30).midpoint() == Vector2s{3.0,3.5} );
	};

	SECTION("One ghost layer"){
		MAKE_MESH(3,4,1,1)
		test(mesh);
	}
}

TEST_CASE( MESH_PREFIX "Edge: domain and ghost cell indices of boundary edges" ){
	auto testOneLayer = [](const MESH_TYPE& mesh){
		SECTION( "domain cell index" ){
			REQUIRE( mesh.edge( 0).domainCell() ==  6 );
			REQUIRE( mesh.edge( 1).domainCell() ==  7 );
			REQUIRE( mesh.edge( 2).domainCell() ==  8 );
			
			REQUIRE( mesh.edge(12).domainCell() == 21 );
			REQUIRE( mesh.edge(13).domainCell() == 22 );
			REQUIRE( mesh.edge(14).domainCell() == 23 );
			
			REQUIRE( mesh.edge(15).domainCell() ==  6 );
			REQUIRE( mesh.edge(19).domainCell() == 11 );
			REQUIRE( mesh.edge(23).domainCell() == 16 );
			REQUIRE( mesh.edge(27).domainCell() == 21 );
			
			REQUIRE( mesh.edge(18).domainCell() ==  8 );
			REQUIRE( mesh.edge(22).domainCell() == 13 );
			REQUIRE( mesh.edge(26).domainCell() == 18 );
			REQUIRE( mesh.edge(30).domainCell() == 23 );
		}
		SECTION( "ghost cell index" ){
			REQUIRE( mesh.edge( 0).ghostCell() ==  1 );
			REQUIRE( mesh.edge( 1).ghostCell() ==  2 );
			REQUIRE( mesh.edge( 2).ghostCell() ==  3 );
			
			REQUIRE( mesh.edge(12).ghostCell() == 26 );
			REQUIRE( mesh.edge(13).ghostCell() == 27 );
			REQUIRE( mesh.edge(14).ghostCell() == 28 );
			
			REQUIRE( mesh.edge(15).ghostCell() ==  5 );
			REQUIRE( mesh.edge(19).ghostCell() == 10 );
			REQUIRE( mesh.edge(23).ghostCell() == 15 );
			REQUIRE( mesh.edge(27).ghostCell() == 20 );
			
			REQUIRE( mesh.edge(18).ghostCell() ==  9 );
			REQUIRE( mesh.edge(22).ghostCell() == 14 );
			REQUIRE( mesh.edge(26).ghostCell() == 19 );
			REQUIRE( mesh.edge(30).ghostCell() == 24 );
		}
	};

	auto testTwoLayers = [](const MESH_TYPE& mesh){
		SECTION( "domain cell index" ){
			REQUIRE( mesh.edge( 0).domainCell() == 16 );
			REQUIRE( mesh.edge( 1).domainCell() == 17 );
			REQUIRE( mesh.edge( 2).domainCell() == 18 );
			
			REQUIRE( mesh.edge(12).domainCell() == 37 );
			REQUIRE( mesh.edge(13).domainCell() == 38 );
			REQUIRE( mesh.edge(14).domainCell() == 39 );
			
			REQUIRE( mesh.edge(15).domainCell() == 16 );
			REQUIRE( mesh.edge(19).domainCell() == 23 );
			REQUIRE( mesh.edge(23).domainCell() == 30 );
			REQUIRE( mesh.edge(27).domainCell() == 37 );
			
			REQUIRE( mesh.edge(18).domainCell() == 18 );
			REQUIRE( mesh.edge(22).domainCell() == 25 );
			REQUIRE( mesh.edge(26).domainCell() == 32 );
			REQUIRE( mesh.edge(30).domainCell() == 39 );
		}
		SECTION( "ghost cell index" ){
			REQUIRE( mesh.edge( 0).ghostCell() ==  9 );
			REQUIRE( mesh.edge( 1).ghostCell() == 10 );
			REQUIRE( mesh.edge( 2).ghostCell() == 11 );
			
			REQUIRE( mesh.edge(12).ghostCell() == 44 );
			REQUIRE( mesh.edge(13).ghostCell() == 45 );
			REQUIRE( mesh.edge(14).ghostCell() == 46 );
			
			REQUIRE( mesh.edge(15).ghostCell() == 15 );
			REQUIRE( mesh.edge(19).ghostCell() == 22 );
			REQUIRE( mesh.edge(23).ghostCell() == 29 );
			REQUIRE( mesh.edge(27).ghostCell() == 36 );
			
			REQUIRE( mesh.edge(18).ghostCell() == 19 );
			REQUIRE( mesh.edge(22).ghostCell() == 26 );
			REQUIRE( mesh.edge(26).ghostCell() == 33 );
			REQUIRE( mesh.edge(30).ghostCell() == 40 );
		}
	};

	SECTION("One ghost layer"){
		MAKE_MESH(3,4,1,1)
		testOneLayer(mesh);
	}
}
